#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <assert.h>
#include <pthread.h>

#include "defs.h"
#include "tools.h"

static struct Msg msg;

void * msg_loop()
{
    printf("Entering main loop ...\n");
    int cnt;
    FD_ZERO(&in_set);
    while (1)
    {
        max_fd = v6sockfd;
        FD_SET(v6sockfd, &in_set);
        for (uinfoptr le = utable_he; le != NULL; le = le->pNext)
        {
            FD_SET(le->fd, &in_set);
            if (le->fd > max_fd) max_fd = le->fd;
        }
        cnt = select(max_fd + 1, &in_set, NULL, NULL, NULL);
        if (cnt < 0)
        {
            perror("select listen");
            exit(errno);
        }
        if (FD_ISSET(v6sockfd, &in_set))
        {
            // client request connection
            // 建立用户表项
            p_time_prompt("CONNECT");
            uinfoptr rt = (uinfoptr)malloc(sizeof(User_Info_Table));
            struct sockaddr_in6 client_addr6;
            socklen_t addr_len = sizeof(struct sockaddr_in6);
            if ((rt->fd = accept(v6sockfd,
                            (struct sockaddr *)&client_addr6,
                            &addr_len)) < 0)
            {
                perror("accept new client");
                continue;
            }
            memcpy(&(rt->v6addr), &(client_addr6.sin6_addr),
                    sizeof(struct in6_addr));
            rt->count = 20;
            rt->secs = time(NULL);
            rt->pNext = utable_he;
            utable_he = rt;
            p_v6addr(&rt->v6addr);
            printf("\n");
        }
        uinfoptr le = utable_he;
        for (; le != NULL; le = le->pNext)
        {
            // 只处理活跃通道
            if (!FD_ISSET(le->fd, &in_set)) continue;
            int nread;
            if (ioctl(le->fd, FIONREAD, &nread) < 0)
            {
                perror("buffer dectect");
                exit(errno);
            }
            if (nread != 0)
                // 正常连接
            {
                int len = read_msg(le->fd, &msg);
                p_time_prompt("RECV");
                printf("fd:%d, len=%d, msg_len=%d\n", le->fd, len, msg.length);
                p_buf(&msg, 32, MSGLENGTH);
                if (len < 0)
                {
                    perror("read_msg");
                }
                if (len != msg.length)
                {
                    printf("unfit error, len=%d, msg.length=%d\n",
                        len, msg.length);
                    continue;
                }
                int ind;
                switch (msg.type)
                {
                    case 100: // IP请求
                        // 分配v4地址
                        for (ind = 1; ind < 128; ++ind)
                            if (0 == ipaddr[ind].status) break;
                        ipaddr[ind].status = 1;
                        sprintf(ipaddr[ind].addr, "13.8.0.%d", ind + 1);
                        // 产生消息包
                        msg.length = htonl(MSGLENGTH);
                        msg.type = 101;
                        memset(msg.data, 0, sizeof(msg.data));
                        sprintf(msg.data, "%s %s %s %s %s",
                                ipaddr[ind].addr,
                                gu_route, gu_dns[0],
                                gu_dns[1], gu_dns[2]);
                        write_all(le->fd, &msg, MSGLENGTH);
                        // 记录分配信息，补充更新用户表
                        inet_pton(AF_INET, ipaddr[ind].addr,
                                &(le->v4addr));
                        le->count = 20;
                        le->secs = time(NULL);
                        p_time_prompt("ALLOC");
                        printf("%s\n", msg.data);
                        p_buf(&msg, 32, MSGLENGTH);
                        break;
                    case 102: // 上网请求
                        write_all(tun, msg.data, msg.length - 5);
                        break;
                    case 104: // 心跳包
                        le->secs = time(NULL);
                        break;
                    default:
                        p_time_prompt("TYPE ERR");
                        printf("%d\n", msg.type);
                        break;
                }
            }
            else // 客户端断开
            {
                p_time_prompt("CUT BY USER");
                printf("fd:%d\n", le->fd);
                if (remove_client(le))
                {
                    perror("client disconnect");
                    exit(errno);
                }
            }
        }
    }
}
